﻿/********************************************************
 * Module Name    : 
 * Purpose        : Inventory Movement Model
 * Class Used     : X_M_Movement, DocAction(Interface)
 * Chronological Development
 * Veena         26-Oct-2009
 ******************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using VAdvantage.Classes;
using VAdvantage.Common;
using VAdvantage.Process; using VAdvantage.ProcessEngine;
using VAdvantage.DataBase;
using VAdvantage.Utility;
using System.Data;
using System.IO;
using System.Data.SqlClient;
using VAdvantage.Logging;

namespace VAdvantage.Model
{
    /// <summary>
    /// Inventory Movement Model
    /// </summary>
    public class MMovement : X_M_Movement, DocAction
    {
        /**	Lines						*/
        private MMovementLine[] _lines = null;
        /** Confirmations				*/
        private MMovementConfirm[] _confirms = null;
        /**	Process Message 			*/
        private String _processMsg = null;
        /**	Just Prepared Flag			*/
        private Boolean _justPrepared = false;

        /// <summary>
        /// Standard Constructor
        /// </summary>
        /// <param name="ctx">context</param>
        /// <param name="M_Movement_ID">id</param>
        /// <param name="trxName">transaction</param>
        public MMovement(Ctx ctx, int M_Movement_ID, String trxName)
            : base(ctx, M_Movement_ID, trxName)
        {
            if (M_Movement_ID == 0)
            {
                //	SetC_DocType_ID (0);
                SetDocAction(DOCACTION_Complete);	// CO
                SetDocStatus(DOCSTATUS_Drafted);	// DR
                SetIsApproved(false);
                SetIsInTransit(false);
                SetMovementDate(new DateTime(CommonFunctions.CurrentTimeMillis()));	// @#Date@
                SetPosted(false);
                base.SetProcessed(false);
            }
        }

        /// <summary>
        /// Load Constructor
        /// </summary>
        /// <param name="ctx">context</param>
        /// <param name="dr">data row</param>
        /// <param name="trxName">transation</param>
        public MMovement(Ctx ctx, DataRow dr, String trxName)
            : base(ctx, dr, trxName)
        {
        }

        /// <summary>
        /// Get Lines
        /// </summary>
        /// <param name="requery">requery</param>
        /// <returns>array of lines</returns>
        public MMovementLine[] GetLines(Boolean requery)
        {
            if (_lines != null && !requery)
                return _lines;
            //
            List<MMovementLine> list = new List<MMovementLine>();
            String sql = "SELECT * FROM M_MovementLine WHERE M_Movement_ID=@moveid ORDER BY Line";
            try
            {
                SqlParameter[] param = new SqlParameter[1];
                param[0] = new SqlParameter("@moveid", GetM_Movement_ID());

                DataSet ds = DB.ExecuteDataset(sql, param, Get_TrxName());
                foreach (DataRow dr in ds.Tables[0].Rows)
                {
                    list.Add(new MMovementLine(GetCtx(), dr, Get_TrxName()));
                }
            }
            catch (Exception e)
            {
                log.Log(Level.SEVERE, "GetLines", e);
            }

            _lines = new MMovementLine[list.Count];
            _lines = list.ToArray();
            return _lines;
        }

        /// <summary>
        /// Get Confirmations
        /// </summary>
        /// <param name="requery">requery</param>
        /// <returns>array of confirmations</returns>
        public MMovementConfirm[] GetConfirmations(Boolean requery)
        {
            if (_confirms != null && !requery)
                return _confirms;

            List<MMovementConfirm> list = new List<MMovementConfirm>();
            String sql = "SELECT * FROM M_MovementConfirm WHERE M_Movement_ID=@moveid";
            try
            {
                SqlParameter[] param = new SqlParameter[1];
                param[0] = new SqlParameter("@moveid", GetM_Movement_ID());

                DataSet ds = DB.ExecuteDataset(sql, param, Get_TrxName());
                foreach (DataRow dr in ds.Tables[0].Rows)
                {
                    list.Add(new MMovementConfirm(GetCtx(), dr, Get_TrxName()));
                }
            }
            catch (Exception e)
            {
                log.Log(Level.SEVERE, "GetConfirmations", e);
            }

            _confirms = new MMovementConfirm[list.Count];
            _confirms = list.ToArray();
            return _confirms;
        }

        /// <summary>
        /// Add to Description
        /// </summary>
        /// <param name="description">text</param>
        public void AddDescription(String description)
        {
            String desc = GetDescription();
            if (desc == null)
                SetDescription(description);
            else
                SetDescription(desc + " | " + description);
        }

        /// <summary>
        /// Get Document Info
        /// </summary>
        /// <returns>document info (untranslated)</returns>
        public String GetDocumentInfo()
        {
            MDocType dt = MDocType.Get(GetCtx(), GetC_DocType_ID());
            return dt.GetName() + " " + GetDocumentNo();
        }

        /// <summary>
        /// Create PDF
        /// </summary>
        /// <returns>File or null</returns>
        public FileInfo CreatePDF()
        {
            try
            {
                string fileName = Get_TableName() + Get_ID() + "_" + CommonFunctions.GenerateRandomNo()
                                    + ".txt"; //.pdf
                string filePath = Path.GetTempPath() + fileName;

                FileInfo temp = new FileInfo(filePath);
                if (!temp.Exists)
                {
                    return CreatePDF(temp);
                }
            }
            catch (Exception e)
            {
                log.Severe("Could not create PDF - " + e.Message);
            }
            return null;
        }

        /// <summary>
        /// Create PDF file
        /// </summary>
        /// <param name="file">output file</param>
        /// <returns>file if success</returns>
        public FileInfo CreatePDF(FileInfo file)
        {
            //	ReportEngine re = ReportEngine.Get (GetCtx(), ReportEngine.INVOICE, GetC_Invoice_ID());
            //	if (re == null)
            return null;
            //	return re.GetPDF(file);
        }

        /// <summary>
        /// Before Save
        /// </summary>
        /// <param name="newRecord">new</param>
        /// <returns>true if success</returns>
        protected override Boolean BeforeSave(Boolean newRecord)
	    {
		    if (GetC_DocType_ID() == 0)
		    {
			    MDocType[] types = MDocType.GetOfDocBaseType(GetCtx(), MDocBaseType.DOCBASETYPE_MATERIALMOVEMENT);
			    if (types.Length > 0)	//	Get first
				    SetC_DocType_ID(types[0].GetC_DocType_ID());
			    else
			    {
				    log.SaveError("Error", Msg.ParseTranslation(GetCtx(), "@NotFound@ @C_DocType_ID@"));
				    return false;
			    }
		    }
		    return true;
	    }

        /// <summary>
        /// Set Processed.
        ///	Propergate to Lines/Taxes
        /// </summary>
        /// <param name="processed">processed</param>
        public void SetProcessed(Boolean processed)
        {
            base.SetProcessed(processed);
            if (Get_ID() == 0)
                return;
            String sql = "UPDATE M_MovementLine SET Processed='"
                + (processed ? "Y" : "N")
                + "' WHERE M_Movement_ID=" + GetM_Movement_ID();
            int noLine = DB.ExecuteQuery(sql, null, Get_TrxName());
            _lines = null;
            log.Fine("Processed=" + processed + " - Lines=" + noLine);
        }

        /// <summary>
        /// Process document
        /// </summary>
        /// <param name="processAction">document action</param>
        /// <returns>true if performed</returns>
        public Boolean ProcessIt(String processAction)
        {
            _processMsg = null;
            DocumentEngine engine = new DocumentEngine(this, GetDocStatus());
            return engine.ProcessIt(processAction, GetDocAction());
        }

        /// <summary>
        /// Unlock Document.
        /// </summary>
        /// <returns>true if success</returns>
        public Boolean UnlockIt()
        {
            log.Info(ToString());
            SetProcessing(false);
            return true;
        }

        /// <summary>
        /// Invalidate Document
        /// </summary>
        /// <returns>true if success</returns>
        public Boolean InvalidateIt()
        {
            log.Info(ToString());
            SetDocAction(DOCACTION_Prepare);
            return true;
        }

        /// <summary>
        /// Prepare Document
        /// </summary>
        /// <returns>new status (In Progress or Invalid)</returns>
        public String PrepareIt()
        {
            log.Info(ToString());
            _processMsg = ModelValidationEngine.Get().FireDocValidate(this, ModalValidatorVariables.DOCTIMING_BEFORE_PREPARE);
            if (_processMsg != null)
                return DocActionVariables.STATUS_INVALID;
            MDocType dt = MDocType.Get(GetCtx(), GetC_DocType_ID());

            //	Std Period open?
            if (!MPeriod.IsOpen(GetCtx(), GetMovementDate(), dt.GetDocBaseType()))
            {
                _processMsg = "@PeriodClosed@";
                return DocActionVariables.STATUS_INVALID;
            }
            MMovementLine[] lines = GetLines(false);
            if (lines.Length == 0)
            {
                _processMsg = "@NoLines@";
                return DocActionVariables.STATUS_INVALID;
            }
            //	Add up Amounts

            /* nnayak - Bug 1750251 : check material policy and update storage
               at the line level in completeIt()*/
            //checkMaterialPolicy();

            //	Confirmation
            if (dt.IsInTransit())
                CreateConfirmation();

            _justPrepared = true;
            if (!DOCACTION_Complete.Equals(GetDocAction()))
                SetDocAction(DOCACTION_Complete);
            return DocActionVariables.STATUS_INPROGRESS;
        }

        /// <summary>
        /// Create Movement Confirmation
        /// </summary>
        private void CreateConfirmation()
        {
            MMovementConfirm[] confirmations = GetConfirmations(false);
            if (confirmations.Length > 0)
                return;

            //	Create Confirmation
            MMovementConfirm.Create(this, false);
        }

        /// <summary>
        /// Approve Document
        /// </summary>
        /// <returns>true if success</returns>
        public Boolean ApproveIt()
        {
            log.Info(ToString());
            SetIsApproved(true);
            return true;
        }

        /// <summary>
        /// Reject Approval
        /// </summary>
        /// <returns>true if success</returns>
        public Boolean RejectIt()
        {
            log.Info(ToString());
            SetIsApproved(false);
            return true;
        }

        /// <summary>
        /// Complete Document
        /// </summary>
        /// <returns>new status (Complete, In Progress, Invalid, Waiting ..)</returns>
        public String CompleteIt()
	    {
		    //	Re-Check
		    if (!_justPrepared)
		    {
			    String status = PrepareIt();
			    if (!DocActionVariables.STATUS_INPROGRESS.Equals(status))
				    return status;
		    }
    		
		    //	Outstanding (not processed) Incoming Confirmations ?
		    MMovementConfirm[] confirmations = GetConfirmations(true);
		    for (int i = 0; i < confirmations.Length; i++)
		    {
			    MMovementConfirm confirm = confirmations[i];
			    if (!confirm.IsProcessed())
			    {
				    _processMsg = "Open: @M_MovementConfir_ID@ - " 
					    + confirm.GetDocumentNo();
				    return DocActionVariables.STATUS_INPROGRESS;
			    }
		    }
    		
		    //	Implicit Approval
		    if (!IsApproved())
			    ApproveIt();
		    log.Info(ToString());
    		
		    //
		    MMovementLine[] lines = GetLines(false);
		    for (int i = 0; i < lines.Length; i++)
		    {
			    MMovementLine line = lines[i];
    			
			    /* nnayak - Bug 1750251 : If you have multiple lines for the same product
			    in the same Sales Order, or if the generate shipment process was generating
			    multiple shipments for the same product in the same run, the first layer 
			    was Getting consumed by all the shipments. As a result, the first layer had
			    negative Inventory even though there were other positive layers. */
			    CheckMaterialPolicy(line);
    			
			    MTransaction trxFrom = null; 
			    if (line.GetM_AttributeSetInstance_ID() == 0)
			    {
				    MMovementLineMA[] mas = MMovementLineMA.Get(GetCtx(),
					    line.GetM_MovementLine_ID(), Get_TrxName());
				    for (int j = 0; j < mas.Length; j++)
				    {
					    MMovementLineMA ma = mas[j];
					    //
					    MStorage storageFrom = MStorage.Get(GetCtx(), line.GetM_Locator_ID(), 
						    line.GetM_Product_ID(), ma.GetM_AttributeSetInstance_ID(), Get_TrxName());
					    if (storageFrom == null)
						    storageFrom = MStorage.GetCreate(GetCtx(), line.GetM_Locator_ID(), 
							    line.GetM_Product_ID(), ma.GetM_AttributeSetInstance_ID(), Get_TrxName());
					    //
					    MStorage storageTo = MStorage.Get(GetCtx(), line.GetM_LocatorTo_ID(), 
						    line.GetM_Product_ID(), ma.GetM_AttributeSetInstance_ID(), Get_TrxName());
					    if (storageTo == null)
						    storageTo = MStorage.GetCreate(GetCtx(), line.GetM_LocatorTo_ID(), 
							    line.GetM_Product_ID(), ma.GetM_AttributeSetInstance_ID(), Get_TrxName());
					    //
					    storageFrom.SetQtyOnHand(Decimal.Subtract(storageFrom.GetQtyOnHand(), ma.GetMovementQty()));
					    if (!storageFrom.Save(Get_TrxName()))
					    {
						    _processMsg = "Storage From not updated (MA)";
						    return DocActionVariables.STATUS_INVALID;
					    }
					    //
					    storageTo.SetQtyOnHand(Decimal.Add(storageTo.GetQtyOnHand(), ma.GetMovementQty()));
					    if (!storageTo.Save(Get_TrxName()))
					    {
						    _processMsg = "Storage To not updated (MA)";
						    return DocActionVariables.STATUS_INVALID;
					    }
    				
					    //
					    trxFrom = new MTransaction (GetCtx(), line.GetAD_Org_ID(), 
						    MTransaction.MOVEMENTTYPE_MovementFrom,
						    line.GetM_Locator_ID(), line.GetM_Product_ID(), ma.GetM_AttributeSetInstance_ID(),
						    Decimal.Negate(ma.GetMovementQty()), GetMovementDate(), Get_TrxName());
					    trxFrom.SetM_MovementLine_ID(line.GetM_MovementLine_ID());
					    if (!trxFrom.Save())
					    {
						    _processMsg = "Transaction From not inserted (MA)";
						    return DocActionVariables.STATUS_INVALID;
					    }
					    //
					    MTransaction trxTo = new MTransaction (GetCtx(), line.GetAD_Org_ID(), 
						    MTransaction.MOVEMENTTYPE_MovementTo,
						    line.GetM_LocatorTo_ID(), line.GetM_Product_ID(), ma.GetM_AttributeSetInstance_ID(),
						    ma.GetMovementQty(), GetMovementDate(), Get_TrxName());
					    trxTo.SetM_MovementLine_ID(line.GetM_MovementLine_ID());
					    if (!trxTo.Save())
					    {
						    _processMsg = "Transaction To not inserted (MA)";
						    return DocActionVariables.STATUS_INVALID;
					    }
				    }
			    }
			    //	Fallback - We have ASI
			    if (trxFrom == null)
			    {
				    MStorage storageFrom = MStorage.Get(GetCtx(), line.GetM_Locator_ID(), 
					    line.GetM_Product_ID(), line.GetM_AttributeSetInstance_ID(), Get_TrxName());
				    if (storageFrom == null)
					    storageFrom = MStorage.GetCreate(GetCtx(), line.GetM_Locator_ID(), 
						    line.GetM_Product_ID(), line.GetM_AttributeSetInstance_ID(), Get_TrxName());
				    //
				    MStorage storageTo = MStorage.Get(GetCtx(), line.GetM_LocatorTo_ID(), 
					    line.GetM_Product_ID(), line.GetM_AttributeSetInstanceTo_ID(), Get_TrxName());
				    if (storageTo == null)
					    storageTo = MStorage.GetCreate(GetCtx(), line.GetM_LocatorTo_ID(), 
						    line.GetM_Product_ID(), line.GetM_AttributeSetInstanceTo_ID(), Get_TrxName());
				    //
				    storageFrom.SetQtyOnHand(Decimal.Subtract(storageFrom.GetQtyOnHand(), line.GetMovementQty()));
				    if (!storageFrom.Save(Get_TrxName()))
				    {
					    _processMsg = "Storage From not updated";
					    return DocActionVariables.STATUS_INVALID;
				    }
				    //
				    storageTo.SetQtyOnHand(Decimal.Add(storageTo.GetQtyOnHand(), line.GetMovementQty()));
				    if (!storageTo.Save(Get_TrxName()))
				    {
					    _processMsg = "Storage To not updated";
					    return DocActionVariables.STATUS_INVALID;
				    }
    			
				    //
				    trxFrom = new MTransaction (GetCtx(), line.GetAD_Org_ID(), 
					    MTransaction.MOVEMENTTYPE_MovementFrom,
					    line.GetM_Locator_ID(), line.GetM_Product_ID(), line.GetM_AttributeSetInstance_ID(),
					    Decimal.Negate(line.GetMovementQty()), GetMovementDate(), Get_TrxName());
				    trxFrom.SetM_MovementLine_ID(line.GetM_MovementLine_ID());
				    if (!trxFrom.Save())
				    {
					    _processMsg = "Transaction From not inserted";
					    return DocActionVariables.STATUS_INVALID;
				    }
				    //
				    MTransaction trxTo = new MTransaction (GetCtx(), line.GetAD_Org_ID(), 
					    MTransaction.MOVEMENTTYPE_MovementTo,
					    line.GetM_LocatorTo_ID(), line.GetM_Product_ID(), line.GetM_AttributeSetInstanceTo_ID(),
					    line.GetMovementQty(), GetMovementDate(), Get_TrxName());
				    trxTo.SetM_MovementLine_ID(line.GetM_MovementLine_ID());
				    if (!trxTo.Save())
				    {
					    _processMsg = "Transaction To not inserted";
					    return DocActionVariables.STATUS_INVALID;
				    }
			    }	//	Fallback
		    }	//	for all lines
		    //	User Validation
		    String valid = ModelValidationEngine.Get().FireDocValidate(this, ModalValidatorVariables.DOCTIMING_AFTER_COMPLETE);
		    if (valid != null)
		    {
			    _processMsg = valid;
			    return DocActionVariables.STATUS_INVALID;
		    }

		    //
		    SetProcessed(true);
		    SetDocAction(DOCACTION_Close);
		    return DocActionVariables.STATUS_COMPLETED;
	    }

        /// <summary>
        /// Check Material Policy
        /// </summary>
        private void CheckMaterialPolicy()
        {
            int no = MMovementLineMA.DeleteMovementMA(GetM_Movement_ID(), Get_TrxName());
            if (no > 0)
                log.Config("Delete old #" + no);
            MMovementLine[] lines = GetLines(false);

            MClient client = MClient.Get(GetCtx());

            //	Check Lines
            for (int i = 0; i < lines.Length; i++)
            {
                MMovementLine line = lines[i];

                Boolean needSave = false;

                //	Attribute Set Instance
                if (line.GetM_AttributeSetInstance_ID() == 0)
                {
                    MProduct product = MProduct.Get(GetCtx(), line.GetM_Product_ID());
                    MProductCategory pc = MProductCategory.Get(GetCtx(), product.GetM_Product_Category_ID());
                    String MMPolicy = pc.GetMMPolicy();
                    if (MMPolicy == null || MMPolicy.Length == 0)
                        MMPolicy = client.GetMMPolicy();
                    //
                    MStorage[] storages = MStorage.GetAllWithASI(GetCtx(),
                        line.GetM_Product_ID(), line.GetM_Locator_ID(),
                        MClient.MMPOLICY_FiFo.Equals(MMPolicy), Get_TrxName());
                    Decimal qtyToDeliver = line.GetMovementQty();
                    for (int ii = 0; ii < storages.Length; ii++)
                    {
                        MStorage storage = storages[ii];
                        if (ii == 0)
                        {
                            if (storage.GetQtyOnHand().CompareTo(qtyToDeliver) >= 0)
                            {
                                line.SetM_AttributeSetInstance_ID(storage.GetM_AttributeSetInstance_ID());
                                needSave = true;
                                log.Config("Direct - " + line);
                                qtyToDeliver = Envs.ZERO;
                            }
                            else
                            {
                                log.Config("Split - " + line);
                                MMovementLineMA ma = new MMovementLineMA(line,
                                    storage.GetM_AttributeSetInstance_ID(),
                                    storage.GetQtyOnHand());
                                if (!ma.Save())
                                    ;
                                qtyToDeliver = Decimal.Subtract(qtyToDeliver, storage.GetQtyOnHand());
                                log.Fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver);
                            }
                        }
                        else	//	 create Addl material allocation
                        {
                            MMovementLineMA ma = new MMovementLineMA(line,
                                storage.GetM_AttributeSetInstance_ID(),
                                qtyToDeliver);
                            if (storage.GetQtyOnHand().CompareTo(qtyToDeliver) >= 0)
                                qtyToDeliver = Envs.ZERO;
                            else
                            {
                                ma.SetMovementQty(storage.GetQtyOnHand());
                                qtyToDeliver = Decimal.Subtract(qtyToDeliver, storage.GetQtyOnHand());
                            }
                            if (!ma.Save())
                                ;
                            log.Fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver);
                        }
                        if (Envs.Signum(qtyToDeliver) == 0)
                            break;
                    }	//	 for all storages

                    //	No AttributeSetInstance found for remainder
                    if (Envs.Signum(qtyToDeliver) != 0)
                    {
                        MMovementLineMA ma = new MMovementLineMA(line,
                            0, qtyToDeliver);
                        if (!ma.Save())
                            ;
                        log.Fine("##: " + ma);
                    }
                }	//	attributeSetInstance

                if (needSave && !line.Save())
                    log.Severe("NOT saved " + line);
            }	//	for all lines

        }

        /// <summary>
        /// Check Material Policy
        /// </summary>
        /// <param name="line">movement line</param>
        private void CheckMaterialPolicy(MMovementLine line)
        {
            int no = MMovementLineMA.DeleteMovementLineMA(line.GetM_MovementLine_ID(), Get_TrxName());
            if (no > 0)
                log.Config("Delete old #" + no);

            MClient client = MClient.Get(GetCtx());
            Boolean needSave = false;

            //	Attribute Set Instance
            if (line.GetM_AttributeSetInstance_ID() == 0)
            {
                MProduct product = MProduct.Get(GetCtx(), line.GetM_Product_ID());
                MProductCategory pc = MProductCategory.Get(GetCtx(), product.GetM_Product_Category_ID());
                String MMPolicy = pc.GetMMPolicy();
                if (MMPolicy == null || MMPolicy.Length == 0)
                    MMPolicy = client.GetMMPolicy();
                //
                MStorage[] storages = MStorage.GetAllWithASI(GetCtx(),
                    line.GetM_Product_ID(), line.GetM_Locator_ID(),
                    MClient.MMPOLICY_FiFo.Equals(MMPolicy), Get_TrxName());
                Decimal qtyToDeliver = line.GetMovementQty();
                for (int ii = 0; ii < storages.Length; ii++)
                {
                    MStorage storage = storages[ii];
                    if (ii == 0)
                    {
                        if (storage.GetQtyOnHand().CompareTo(qtyToDeliver) >= 0)
                        {
                            line.SetM_AttributeSetInstance_ID(storage.GetM_AttributeSetInstance_ID());
                            needSave = true;
                            log.Config("Direct - " + line);
                            qtyToDeliver = Envs.ZERO;
                        }
                        else
                        {
                            log.Config("Split - " + line);
                            MMovementLineMA ma = new MMovementLineMA(line,
                                storage.GetM_AttributeSetInstance_ID(),
                                storage.GetQtyOnHand());
                            if (!ma.Save())
                                ;
                            qtyToDeliver = Decimal.Subtract(qtyToDeliver, storage.GetQtyOnHand());
                            log.Fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver);
                        }
                    }
                    else	//	 create Addl material allocation
                    {
                        MMovementLineMA ma = new MMovementLineMA(line,
                            storage.GetM_AttributeSetInstance_ID(),
                            qtyToDeliver);
                        if (storage.GetQtyOnHand().CompareTo(qtyToDeliver) >= 0)
                            qtyToDeliver = Envs.ZERO;
                        else
                        {
                            ma.SetMovementQty(storage.GetQtyOnHand());
                            qtyToDeliver = Decimal.Subtract(qtyToDeliver, storage.GetQtyOnHand());
                        }
                        if (!ma.Save())
                            ;
                        log.Fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver);
                    }
                    if (Envs.Signum(qtyToDeliver) == 0)
                        break;
                }	//	 for all storages

                //	No AttributeSetInstance found for remainder
                if (Envs.Signum(qtyToDeliver) != 0)
                {
                    MMovementLineMA ma = new MMovementLineMA(line,
                        0, qtyToDeliver);
                    if (!ma.Save())
                        ;
                    log.Fine("##: " + ma);
                }
            }	//	attributeSetInstance


            if (needSave && !line.Save())
                log.Severe("NOT saved " + line);

        }

        /// <summary>
        /// Void Document.
        /// </summary>
        /// <returns>true if success</returns>
        public Boolean VoidIt()
        {
            log.Info(ToString());
            if (DOCSTATUS_Closed.Equals(GetDocStatus())
                || DOCSTATUS_Reversed.Equals(GetDocStatus())
                || DOCSTATUS_Voided.Equals(GetDocStatus()))
            {
                _processMsg = "Document Closed: " + GetDocStatus();
                return false;
            }

            //	Not Processed
            if (DOCSTATUS_Drafted.Equals(GetDocStatus())
                || DOCSTATUS_Invalid.Equals(GetDocStatus())
                || DOCSTATUS_InProgress.Equals(GetDocStatus())
                || DOCSTATUS_Approved.Equals(GetDocStatus())
                || DOCSTATUS_NotApproved.Equals(GetDocStatus()))
            {
                //	Set lines to 0
                MMovementLine[] lines = GetLines(false);
                for (int i = 0; i < lines.Length; i++)
                {
                    MMovementLine line = lines[i];
                    Decimal old = line.GetMovementQty();
                    if (old.CompareTo(Envs.ZERO) != 0)
                    {
                        line.SetMovementQty(Envs.ZERO);
                        line.AddDescription("Void (" + old + ")");
                        line.Save(Get_TrxName());
                    }
                }
            }
            else
            {
                return ReverseCorrectIt();
            }

            SetProcessed(true);
            SetDocAction(DOCACTION_None);
            return true;
        }

        /// <summary>
        /// Close Document.
        /// </summary>
        /// <returns>true if success</returns>
        public Boolean CloseIt()
        {
            log.Info(ToString());

            //	Close Not delivered Qty
            SetDocAction(DOCACTION_None);
            return true;
        }

        /// <summary>
        /// Reverse Correction
        /// </summary>
        /// <returns>false</returns>
        public Boolean ReverseCorrectIt()
        {
            log.Info(ToString());
            MDocType dt = MDocType.Get(GetCtx(), GetC_DocType_ID());
            if (!MPeriod.IsOpen(GetCtx(), GetMovementDate(), dt.GetDocBaseType()))
            {
                _processMsg = "@PeriodClosed@";
                return false;
            }

            //	Deep Copy
            MMovement reversal = new MMovement(GetCtx(), 0, Get_TrxName());
            CopyValues(this, reversal, GetAD_Client_ID(), GetAD_Org_ID());
            reversal.SetDocStatus(DOCSTATUS_Drafted);
            reversal.SetDocAction(DOCACTION_Complete);
            reversal.SetIsApproved(false);
            reversal.SetIsInTransit(false);
            reversal.SetPosted(false);
            reversal.SetProcessed(false);
            reversal.AddDescription("{->" + GetDocumentNo() + ")");
            if (!reversal.Save())
            {
                _processMsg = "Could not create Movement Reversal";
                return false;
            }

            //	Reverse Line Qty
            MMovementLine[] oLines = GetLines(true);
            for (int i = 0; i < oLines.Length; i++)
            {
                MMovementLine oLine = oLines[i];
                MMovementLine rLine = new MMovementLine(GetCtx(), 0, Get_TrxName());
                CopyValues(oLine, rLine, oLine.GetAD_Client_ID(), oLine.GetAD_Org_ID());
                rLine.SetM_Movement_ID(reversal.GetM_Movement_ID());
                //
                rLine.SetMovementQty(Decimal.Negate(rLine.GetMovementQty()));
                rLine.SetTargetQty(Envs.ZERO);
                rLine.SetScrappedQty(Envs.ZERO);
                rLine.SetConfirmedQty(Envs.ZERO);
                rLine.SetProcessed(false);
                if (!rLine.Save())
                {
                    _processMsg = "Could not create Movement Reversal Line";
                    return false;
                }
            }
            //
            if (!reversal.ProcessIt(DocActionVariables.ACTION_COMPLETE))
            {
                _processMsg = "Reversal ERROR: " + reversal.GetProcessMsg();
                return false;
            }
            reversal.CloseIt();
            reversal.SetDocStatus(DOCSTATUS_Reversed);
            reversal.SetDocAction(DOCACTION_None);
            reversal.Save();
            _processMsg = reversal.GetDocumentNo();

            //	Update Reversed (this)
            AddDescription("(" + reversal.GetDocumentNo() + "<-)");
            SetProcessed(true);
            SetDocStatus(DOCSTATUS_Reversed);	//	may come from void
            SetDocAction(DOCACTION_None);

            return true;
        }

        /// <summary>
        /// Reverse Accrual - none
        /// </summary>
        /// <returns>false</returns>
        public Boolean ReverseAccrualIt()
        {
            log.Info(ToString());
            return false;
        }

        /// <summary>
        /// Re-activate
        /// </summary>
        /// <returns>false</returns>
        public Boolean ReActivateIt()
        {
            log.Info(ToString());
            return false;
        }

        /// <summary>
        /// Get Summary
        /// </summary>
        /// <returns>Summary of Document</returns>
        public String GetSummary()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(GetDocumentNo());
            //	: Total Lines = 123.00 (#1)
            sb.Append(": ")
                .Append(Msg.Translate(GetCtx(), "ApprovalAmt")).Append("=").Append(GetApprovalAmt())
                .Append(" (#").Append(GetLines(false).Length).Append(")");
            //	 - Description
            if (GetDescription() != null && GetDescription().Length > 0)
                sb.Append(" - ").Append(GetDescription());
            return sb.ToString();
        }

        /// <summary>
        /// String Representation
        /// </summary>
        /// <returns>info</returns>
        public override String ToString()
        {
            StringBuilder sb = new StringBuilder("MMovement[");
            sb.Append(Get_ID())
                .Append("-").Append(GetDocumentNo())
                .Append("]");
            return sb.ToString();
        }

        /// <summary>
        /// Get Process Message
        /// </summary>
        /// <returns>clear text error message</returns>
        public String GetProcessMsg()
        {
            return _processMsg;
        }

        /// <summary>
        /// Get Document Owner (Responsible)
        /// </summary>
        /// <returns>AD_User_ID</returns>
        public int GetDoc_User_ID()
        {
            return GetCreatedBy();
        }

        /// <summary>
        /// Get Document Currency
        /// </summary>
        /// <returns>C_Currency_ID</returns>
        public int GetC_Currency_ID()
        {
            //	MPriceList pl = MPriceList.Get(GetCtx(), GetM_PriceList_ID());
            //	return pl.GetC_Currency_ID();
            return 0;
        }



        public Envs.QueryParams GetLineOrgsQueryInfo()
        {
            return null;
        }

        public DateTime? GetDocumentDate()
        {
            return null;
        }

        public string GetDocBaseType()
        {
            return null;
        }
    }
}